package com.alipay.demo.trade.service.impl;

import com.alipay.api.AlipayClient;
import com.alipay.api.AlipayResponse;
import com.alipay.api.request.AlipayTradeCancelRequest;
import com.alipay.api.request.AlipayTradePrecreateRequest;
import com.alipay.api.request.AlipayTradeQueryRequest;
import com.alipay.api.request.AlipayTradeRefundRequest;
import com.alipay.api.response.AlipayTradeCancelResponse;
import com.alipay.api.response.AlipayTradePayResponse;
import com.alipay.api.response.AlipayTradePrecreateResponse;
import com.alipay.api.response.AlipayTradeQueryResponse;
import com.alipay.api.response.AlipayTradeRefundResponse;
import com.alipay.demo.trade.config.Configs;
import com.alipay.demo.trade.model.TradeStatus;
import com.alipay.demo.trade.model.builder.AlipayTradePrecreateContentBuilder;
import com.alipay.demo.trade.model.builder.AlipayTradeQueryCententBuilder;
import com.alipay.demo.trade.model.builder.AlipayTradeRefundContentBuilder;
import com.alipay.demo.trade.model.result.AlipayF2FPrecreateResult;
import com.alipay.demo.trade.model.result.AlipayF2FQueryResult;
import com.alipay.demo.trade.model.result.AlipayF2FRefundResult;
import com.alipay.demo.trade.service.AlipayTradeService;
import com.alipay.demo.trade.service.impl.AbsAlipayService;
import com.alipay.demo.trade.utils.Utils;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

abstract class AbsAlipayTradeService extends AbsAlipayService implements AlipayTradeService {

   protected static ExecutorService executorService = Executors.newCachedThreadPool();
   protected AlipayClient client;


   public AlipayF2FQueryResult queryTradeResult(String outTradeNo) {
      AlipayTradeQueryResponse response = this.tradeQuery(outTradeNo);
      AlipayF2FQueryResult result = new AlipayF2FQueryResult(response);
      if(this.querySuccess(response)) {
         result.setTradeStatus(TradeStatus.SUCCESS);
      } else if(this.tradeError(response)) {
         result.setTradeStatus(TradeStatus.UNKNOWN);
      } else {
         result.setTradeStatus(TradeStatus.FAILED);
      }

      return result;
   }

   protected AlipayTradeQueryResponse tradeQuery(String outTradeNo) {
      AlipayTradeQueryCententBuilder builder = (new AlipayTradeQueryCententBuilder()).setOutTradeNo(outTradeNo);
      this.validateBuilder(builder);
      AlipayTradeQueryRequest request = new AlipayTradeQueryRequest();
      request.setBizContent(builder.toJsonString());
      this.log.info("trade.query bizContent:" + request.getBizContent());
      return (AlipayTradeQueryResponse)this.getResponse(this.client, request);
   }

   public AlipayF2FRefundResult tradeRefund(AlipayTradeRefundContentBuilder builder) {
      this.validateBuilder(builder);
      AlipayTradeRefundRequest request = new AlipayTradeRefundRequest();
      request.setBizContent(builder.toJsonString());
      this.log.info("trade.refund bizContent:" + request.getBizContent());
      AlipayTradeRefundResponse response = (AlipayTradeRefundResponse)this.getResponse(this.client, request);
      AlipayF2FRefundResult result = new AlipayF2FRefundResult(response);
      if(response != null && "10000".equals(response.getCode())) {
         result.setTradeStatus(TradeStatus.SUCCESS);
      } else if(this.tradeError(response)) {
         result.setTradeStatus(TradeStatus.UNKNOWN);
      } else {
         result.setTradeStatus(TradeStatus.FAILED);
      }

      return result;
   }

   public AlipayF2FPrecreateResult tradePrecreate(AlipayTradePrecreateContentBuilder builder) {
      this.validateBuilder(builder);
      AlipayTradePrecreateRequest request = new AlipayTradePrecreateRequest();
      request.setBizContent(builder.toJsonString());
      this.log.info("trade.precreate bizContent:" + request.getBizContent());
      AlipayTradePrecreateResponse response = (AlipayTradePrecreateResponse)this.getResponse(this.client, request);
      AlipayF2FPrecreateResult result = new AlipayF2FPrecreateResult(response);
      if(response != null && "10000".equals(response.getCode())) {
         result.setTradeStatus(TradeStatus.SUCCESS);
      } else if(this.tradeError(response)) {
         result.setTradeStatus(TradeStatus.UNKNOWN);
      } else {
         result.setTradeStatus(TradeStatus.FAILED);
      }

      return result;
   }

   protected AlipayTradeCancelResponse tradeCancel(String outTradeNo) {
      AlipayTradeCancelRequest request = new AlipayTradeCancelRequest();
      String bizContent = "{\'out_trade_no\':\'" + outTradeNo + "\'}";
      request.setBizContent(bizContent);
      this.log.info("trade.cancel bizContent:" + request.getBizContent());
      return (AlipayTradeCancelResponse)this.getResponse(this.client, request);
   }

   protected AlipayTradeQueryResponse loopQueryResult(String outTradeNo) {
      AlipayTradeQueryResponse queryResult = null;

      for(int i = 0; i < Configs.getMaxQueryRetry(); ++i) {
         Utils.sleep(Configs.getQueryDuration());
         AlipayTradeQueryResponse response = this.tradeQuery(outTradeNo);
         if(response != null) {
            if(this.stopQuery(response)) {
               return response;
            }

            queryResult = response;
         }
      }

      return queryResult;
   }

   protected boolean stopQuery(AlipayTradeQueryResponse response) {
      return "10000".equals(response.getCode()) && ("TRADE_FINISHED".equals(response.getTradeStatus()) || "TRADE_SUCCESS".equals(response.getTradeStatus()) || "TRADE_CLOSED".equals(response.getTradeStatus()));
   }

   protected AlipayTradeCancelResponse cancelPayResult(String outTradeNo) {
      AlipayTradeCancelResponse response = this.tradeCancel(outTradeNo);
      if(this.cancelSuccess(response)) {
         return response;
      } else {
         if(this.needRetry(response)) {
            this.log.warn("begin async cancel outTradeNo:" + outTradeNo);
            this.asyncCancel(outTradeNo);
         }

         return response;
      }
   }

   protected void asyncCancel(final String outTradeNo) {
      executorService.submit(new Runnable() {
         public void run() {
            for(int i = 0; i < Configs.getMaxCancelRetry(); ++i) {
               Utils.sleep(Configs.getCancelDuration());
               AlipayTradeCancelResponse response = AbsAlipayTradeService.this.tradeCancel(outTradeNo);
               if(AbsAlipayTradeService.this.cancelSuccess(response) || !AbsAlipayTradeService.this.needRetry(response)) {
                  return;
               }
            }

         }
      });
   }

   protected AlipayTradePayResponse toPayResponse(AlipayTradeQueryResponse response) {
      AlipayTradePayResponse payResponse = new AlipayTradePayResponse();
      payResponse.setCode(this.querySuccess(response)?"10000":"40004");
      StringBuilder msg = (new StringBuilder(response.getMsg())).append(" tradeStatus:").append(response.getTradeStatus());
      payResponse.setMsg(msg.toString());
      payResponse.setSubCode(response.getSubCode());
      payResponse.setSubMsg(response.getSubMsg());
      payResponse.setBody(response.getBody());
      payResponse.setParams(response.getParams());
      payResponse.setBuyerLogonId(response.getBuyerLogonId());
      payResponse.setFundBillList(response.getFundBillList());
      payResponse.setOpenId(response.getOpenId());
      payResponse.setOutTradeNo(response.getOutTradeNo());
      payResponse.setReceiptAmount(response.getReceiptAmount());
      payResponse.setTotalAmount(response.getTotalAmount());
      payResponse.setTradeNo(response.getTradeNo());
      return payResponse;
   }

   protected boolean needRetry(AlipayTradeCancelResponse response) {
      return response == null || "Y".equals(response.getRetryFlag());
   }

   protected boolean querySuccess(AlipayTradeQueryResponse response) {
      return response != null && "10000".equals(response.getCode()) && ("TRADE_SUCCESS".equals(response.getTradeStatus()) || "TRADE_FINISHED".equals(response.getTradeStatus()));
   }

   protected boolean cancelSuccess(AlipayTradeCancelResponse response) {
      return response != null && "10000".equals(response.getCode());
   }

   protected boolean tradeError(AlipayResponse response) {
      return response == null || "20000".equals(response.getCode());
   }
}
